<template>
  <div>
    <input type="file" @change="onFileChange" />
    <button @click="onUploadFile">上传文件</button>
    <div>总上传进度：{{ uploadTotalProgress }}%</div>
    <div v-for="(item, index) in data" :key="index">
      {{ item.chunk_hash }}--{{ index }}，上传进度：{{ item.percentage }}%
    </div>
  </div>
</template>

<script lang="ts" setup>
import { computed, ref } from "vue";
import { CHUNK_SIZE, cutFile, getFileHash } from "./utils/file";
import type { FileChunkResult } from "./utils/worker/chunkFile";
import { request } from "./utils/request";

const file = ref<File | null>(null);
const fileHash = ref<string>("");
const data = ref<Array<FileChunkResult> | null>(null);

const uploadTotalProgress = computed(() => {
  if (!data.value || !file.value) return 0;
  const total = data.value.reduce(
    (prev, item) => prev + (item.percentage / 100) * item.blob.size,
    0
  );
  return Math.floor((total / file.value.size) * 100);
});

const onFileChange = async (event: Event) => {
  const files = (event.target as HTMLInputElement).files!;
  file.value = files[0];
};

/** 上传文件 */
const onUploadFile = async () => {
  if (!file.value) return;
  const result = (await cutFile(file.value)) as Array<FileChunkResult>;
  data.value = result;
  const hash = await getFileHash(result);
  fileHash.value = hash;
  const { shouldUpload, uploadedList = [] } = await verifyUpload(
    file.value.name,
    fileHash.value
  );
  if (!shouldUpload) {
    data.value = data.value.map((v) => {
      v.percentage = 100;
      return v;
    });
    alert("服务器中存在了，跳过上传，文件秒传");
    return;
  }
  console.log("已经上传了", uploadedList.length);
  await uploadChunk(uploadedList);
};

/** 校验文件 */
const verifyUpload = async (filename: string, fileHash: string) => {
  const { data } = await request({
    url: "http://localhost:3000/verify",
    method: "post",
    headers: { "Content-Type": "application/json" },
    data: JSON.stringify({
      filename,
      fileHash,
    }),
  });
  return JSON.parse(data);
};

/** 上传分片文件 */
const uploadChunk = async (uploadedList: string[] = []) => {
  if (!file.value || !data.value) return;
  const filename = file.value.name;
  data.value = data.value.map((v, i) => {
    if (uploadedList.includes(`${v.chunk_hash}-${i}`)) {
      v.percentage = 100;
    }
    return v;
  });
  const requestList = data.value
    // 这个来实现断点
    // .filter((_, index) => index < 4)
    .filter(
      (item, index) => !uploadedList.includes(`${item.chunk_hash}-${index}`)
    )
    .map((item, index) => {
      const { blob, chunk_hash } = item;
      const formData = new FormData();
      formData.append("chunk", blob);
      formData.append("chunkHash", `${chunk_hash}-${index}`);
      formData.append("fileHash", fileHash.value);
      formData.append("name", filename);
      return request({
        url: "http://localhost:3000/upload",
        method: "post",
        data: formData,
        onProgress: createProgressHandler(item),
      });
    });
  console.log("共上传多少个", requestList.length);
  await Promise.all(requestList);
  if (requestList.length + uploadedList.length === data.value.length) {
    await mergeRequest();
  }
};

const createProgressHandler = (item: FileChunkResult) => {
  return (event: ProgressEvent) => {
    const progress = Math.floor((event.loaded / event.total) * 100);
    item.percentage = progress;
  };
};

/** 合并文件 */
const mergeRequest = async () => {
  if (!file.value) return;
  await request({
    url: "http://localhost:3000/merge",
    method: "post",
    headers: {
      "Content-Type": "application/json",
    },
    data: JSON.stringify({
      filename: file.value.name,
      fileHash: fileHash.value,
      size: CHUNK_SIZE,
    }),
  });
};
</script>

<style scoped></style>
